package cn.edu.cug.cs.gtl.gsm.jdbc.mysql;

import cn.edu.cug.cs.gtl.common.Identifier;
import cn.edu.cug.cs.gtl.gsm.item.DataItem;
import cn.edu.cug.cs.gtl.gsm.item.IndexItem;
import cn.edu.cug.cs.gtl.gsm.item.MetaItem;
import cn.edu.cug.cs.gtl.gsm.jdbc.JDBCStorageManager;
import cn.edu.cug.cs.gtl.util.StringUtils;

import java.io.IOException;
import java.sql.*;
import java.util.List;

public class MysqlStorageManager extends JDBCStorageManager {
    Connection  connection;
    MetaTable   metaTable;
    IndexTable  indexTable;
    DataTables  dataTables;
    /**
     * @param baseName
     * @param collectionSize
     * @param pageSize
     */
    public MysqlStorageManager(Connection connection , String baseName, long collectionSize, int pageSize, boolean overwrite) throws SQLException, IOException {
        super(baseName, collectionSize, pageSize);
        initial(connection,baseName,collectionSize,pageSize,overwrite);
    }
    public MysqlStorageManager(String  connectionString,String username, String password,
                              String baseName, long collectionSize, int pageSize, boolean overWrite) throws ClassNotFoundException, SQLException, IOException {
        super(baseName, collectionSize, pageSize);
        Class.forName("com.mysql.cj.jdbc.Driver");
        Connection db = DriverManager.getConnection(connectionString, username, password);
        initial(db,baseName,collectionSize,pageSize,overWrite);
    }


    protected void initial(Connection connection , String baseName, long collectionSize, int pageSize, boolean overwrite) throws SQLException, IOException {
        this.connection = connection;
        //确保以baseName同名的Schema必须存在
        createSchema(connection,baseName);
        metaTable = new MetaTable(connection,baseName);
        indexTable = new IndexTable(connection,baseName);
        dataTables = new DataTables(connection,metaItem);
        if(metaTable.exists()){
            MetaItem oldMetaItem = metaTable.select(baseName);
            if(overwrite){
                if(oldMetaItem!=null){
                    //删除MetaTable原来的内容
                    metaTable.delete(baseName);
                    //删除索引表中的数据
                    indexTable.deleteAll();
                    //删除对应的数据表
                    int maxOrder = oldMetaItem.getCollectionOrder(oldMetaItem.getNextPageIdentifier());
                    for(int i=0;i<=maxOrder;++i) {
                        dataTables.setCurrentTableOrder(i);
                        dataTables.deleteAll();
                    }
                }
            }
            else{
                if(oldMetaItem!=null){
                    //读取原来的内容
                    readMetaFromStorage();
                    readIndicesFromStorage();
                }
            }
        }
        else{//如果Meta表不存在
            metaTable.create();
            indexTable.create();
        }

    }

    @Override
    protected void readBufferFromPage(Identifier pageIdentifier) throws IOException {
        dataTables.setCurrentTableOrder(metaItem.getCollectionOrder(pageIdentifier));
        try {
            if(dataTables.exists()){
                DataItem item = dataTables.select(pageIdentifier);
                System.arraycopy(item.getPageContent(),0,this.pageBuffer,0,getPageSize());
            }
            else{
                throw  new IOException("DataItem "+pageIdentifier.toString()+"不存在");
            }
        }
        catch (SQLException e){
            e.printStackTrace();
        }

    }

    @Override
    protected void writeBufferToPage(Identifier pageIdentifier) throws IOException {
        dataTables.setCurrentTableOrder(metaItem.getCollectionOrder(pageIdentifier));
        DataItem item = new DataItem(pageIdentifier, this.pageBuffer);
        try {
            if(dataTables.exists()){
                dataTables.delete(pageIdentifier);
                dataTables.insert(item);
            }
            else{
                dataTables.create();
                dataTables.insert(item);
            }
        }
        catch (SQLException e){
            e.printStackTrace();
        }
    }

    @Override
    protected void readMetaFromStorage() throws IOException {
        try {
            assert(metaTable.exists());//构造函数已经保证该表是存在的
            MetaItem oldMetaItem = metaTable.select(getBaseName());
            if(oldMetaItem!=null){
                metaItem.copyFrom(oldMetaItem);
            }
        }
        catch (SQLException e){
            e.printStackTrace();
        }
    }

    @Override
    protected void writeMetaToStorage() throws IOException {
        try {
            assert(metaTable.exists());//构造函数已经保证该表是存在的
            MetaItem oldMetaItem = metaTable.select(getBaseName());
            if(oldMetaItem!=null){
                metaTable.update(getBaseName(),metaItem);
            }
            else{
                metaTable.insert(getBaseName(),metaItem);
            }
        }
        catch (SQLException e){
            e.printStackTrace();
        }
    }

    @Override
    protected void readIndicesFromStorage() throws IOException {
        try {
            assert(indexTable.exists());//构造函数已经保证该表是存在的
            List<IndexItem> ls = indexTable.selectAll();
            pageMap.clear();
            for(IndexItem i: ls){
                pageMap.put(i.getExternalPageIdentifier(),i);
            }
        }
        catch (SQLException e){
            e.printStackTrace();
        }
    }

    @Override
    protected void writeIndicesToStorage() throws IOException {
        try {
            assert(indexTable.exists());//构造函数已经保证该表是存在的
            indexTable.deleteAll();
            for(IndexItem i: pageMap.values()){
                indexTable.insert(i);
            }
        }
        catch (SQLException e){
            e.printStackTrace();
        }
    }

    @Override
    protected void closeStorages() throws IOException {
        try {
            connection.close();
        }
        catch (SQLException e){
            e.printStackTrace();
        }
    }

    /**
     * 判断数据表是否存在
     * @param connection
     * @param tableName
     * @return
     */
    public static boolean tableExists(Connection connection, String tableName){
        String sql="select count(*) from information_schema.TABLES where TABLE_NAME = ?";
        try {
            PreparedStatement statement = connection.prepareStatement(sql);

            statement.setString(1, tableName);//tablename

            ResultSet r = statement.executeQuery();
            int c = 0;
            while (r.next()) {
                c = r.getInt(1);
            }
            if (c != 0)
                return true;
        }
        catch (Exception e){
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 判断模式是否存在
     * @param connection
     * @param schemaName
     * @return
     */
    public static boolean schemaExists(Connection connection, String schemaName){
        String sql = "select count(*) from information_schema.SCHEMATA where SCHEMA_NAME = ?";
        try {
            PreparedStatement statement = connection.prepareStatement(sql);
            statement.setString(1, schemaName);//schemaname
            ResultSet r = statement.executeQuery();
            int c = 0;
            while (r.next()) {
                c = r.getInt(1);
            }
            if (c != 0)
                return true;
        }
        catch (Exception e){
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 创建模式，如果存在则忽略
     * @param connection
     * @param schemaName
     * @return
     */
    public static boolean createSchema(Connection connection, String schemaName){
        try {
            Statement statement=null;
            String sql=null;
            if(schemaExists(connection,schemaName)==false) {
                sql = "CREATE SCHEMA IF NOT EXISTS " + schemaName;
                statement = connection.createStatement();
                statement.execute(sql);
                statement.close();
            }
            else {
                return true;
            }
        }
        catch (Exception e){
            e.printStackTrace();
        }
        return false;
    }

    public static boolean dropSchema(Connection connection, String schemaName){
        if(schemaExists(connection,schemaName)) {
            try {
                String sql = "DROP SCHEMA IF EXISTS " + schemaName;
                Statement statement = connection.createStatement();
                statement.execute(sql);
                statement.close();
                return true;
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return false;
    }


}
